package com.mokairui.share_model_nolock;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description 共享模式之无锁介绍:
 *                  有锁 -> 管程(悲观锁) 适合CPU核心数少的场景,因为它将线程停止了,竞争激烈也适合用有锁的情况
 *                  无锁 -> 乐观锁 适合CPU核心数较多多于线程数,因为线程没有停止,线程一直保持运行状态
 * @Author Mokairui
 * @Since 2021/10/4
 */
public interface Account {
    // 获取余额
    Integer getBalance();
    // 取款
    void withdraw(Integer amount);

    /**
     * 方法内启动1000个线程, 每个线程做 -10 元的操作
     * 如果初始金额为10000那么正确的结果应该是0
     *
     * @param account
     */
    static void demo(Account account) {
        List<Thread> td = new ArrayList<>();
        long startTime = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            td.add(new Thread(() -> {
                account.withdraw(10);
            }));
        }
        td.forEach(Thread::start);
        td.forEach(thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long endTime = System.nanoTime();
        System.out.println(String.format("%s, cost: %s ms", account.getBalance(), (endTime - startTime)/1000_000));
    }
}

class TestDemo {
    public static void main(String[] args) {
        // 有锁 效率没有无锁的快
        AccountLock accountLock = new AccountLock(10000);
        Account.demo(accountLock);
        // 无锁 效率更快
        AccountCAS accountCAS = new AccountCAS(10000);
        Account.demo(accountCAS);
    }
}

/**
 * 加锁的实现方式(悲观锁)
 */
class AccountLock implements Account {
    private int balance;

    public AccountLock(int balance) {
        this.balance = balance;
    }

    @Override
    public Integer getBalance() {
        synchronized (this) {
            return balance;
        }
    }

    @Override
    public void withdraw(Integer amount) {
        synchronized (this) {
            balance -= amount;
        }
    }
}

/**
 * 不加锁的实现方式(乐观锁)
 */
class AccountCAS implements Account {

    private AtomicInteger balance;

    public AccountCAS(Integer balance) {
        this.balance = new AtomicInteger(balance);
    }

    @Override
    public Integer getBalance() {
        return balance.get();
    }

    @Override
    public void withdraw(Integer amount) {
        /*while (true) {
            int prev = balance.get();
            int next = prev - amount;
            if (balance.compareAndSet(prev, next)) {
                break;
            }
        }*/
        balance.getAndAdd(-1 * amount);
    }
}
